package com.ztesoft.ip.webservice;

import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;

import com.alibaba.druid.stat.DruidStatService;
import com.alibaba.druid.support.http.util.IPAddress;
import com.alibaba.druid.support.http.util.IPRange;
import com.alibaba.druid.util.Utils;

import ci.web.annotaction.Router;
import ci.web.core.CiContext;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.base64.Base64;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.codec.http.cookie.DefaultCookie;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

/**
 * druid-serverlet-simple
 * 
 * @see com.alibaba.druid.support.http.ResourceServlet
 * @see https
 *      ://github.com/alibaba/druid/tree/master/src/main/java/com/alibaba/druid
 *      /support/http
 * @author Administrator
 *
 */
@Router("druid")
public class DruidStat {
	private static InternalLogger logger = InternalLoggerFactory.getInstance(DruidStat.class);

	public static final String SESSION_USER_KEY = "druid-user";
	public static final String PARAM_NAME_USERNAME = "loginUsername";
	public static final String PARAM_NAME_PASSWORD = "loginPassword";
	public static final String PARAM_NAME_ALLOW = "allow";
	public static final String PARAM_NAME_DENY = "deny";
	public static final String PARAM_REMOTE_ADDR = "remoteAddress";

	private DruidStatService statService = DruidStatService.getInstance();
	//private String resoucePath = new File("resouce/support/http/resources").getAbsolutePath();
	private String resoucePath =  "support/http/resources";

	protected String username = null;
	protected String password = null;
	protected List<IPRange> allowList = new ArrayList<IPRange>();
	protected List<IPRange> denyList = new ArrayList<IPRange>();

	private String basePath = "/druid";

	public DruidStat() {
		// 看情况= 读取配置
		// username = "zhh";
		// password = "pwd";
		// addIpRule("127.0.0.1,192.168.1.100", allowList);
		// addIpRule("192.168.1.102", denyList);
	}

	private void redirect(CiContext ctx, String page) {
		ctx.redirect(basePath + '/' + page);
	}

	@Router("*")
	public void api(CiContext ctx) throws IOException {
		if (ctx.path().indexOf('.') < 0 && ctx.path().endsWith("/") == false) {
			redirect(ctx, "index.html");
			return;
		}
		if (!isPermittedRequest(getRemoteAddress(ctx))) {
			returnResourceFile(ctx, "/nopermit.html");
			return;
		}
		if (isRequireAuth() //
				&& !ContainsUser(ctx)//
		) {
			if (!(ctx.path().endsWith("/login.html") //
					|| ctx.path().endsWith(".js")//
					|| ctx.path().endsWith(".css") //
			|| ctx.path().endsWith(".jpg"))) {
				redirect(ctx, "login.html");
				return;
			}
		} else if (isRequireAuth() && ctx.path().endsWith("/login.html")) {
			redirect(ctx, "index.html");
			return;
		}
		if (ctx.path().endsWith(".json") == false) {
			returnResourceFile(ctx);
			return;
		}
		String url = ctx.uri();
		int idx = url.lastIndexOf('/');
		url = url.substring(idx);
		// System.out.println(url);
		String ret = statService.service(url);
		ctx.send(ret);
	}

	// @Router("/submitLogin")
	public void submitLogin(CiContext ctx) {
		String usernameParam = ctx.params().getString(PARAM_NAME_USERNAME);
		String passwordParam = ctx.params().getString(PARAM_NAME_PASSWORD);
		if (username.equals(usernameParam) && password.equals(passwordParam)) {
			putSessionUser(ctx);
			ctx.send("success");
		} else {
			ctx.send("error");
		}
	}

	protected boolean isRequireAuth() {
		return this.username != null;
	}

	private void returnResourceFile(CiContext ctx, String filePath) throws IOException {
		if (filePath.endsWith("/")) {
			filePath = filePath + "index.html";
		}
		if (filePath.endsWith(".jpg")) {
			byte[] bytes = Utils.readByteArrayFromResource(filePath);
			if (bytes != null) {
				ctx.send(bytes);
			}
			return;
		}
		String text = Utils.readFromResource(filePath);
		if (text == null) {
			ctx.sendError("404");
			return;
		}
		if (filePath.endsWith(".css")) {
			ctx.setContentType("text/css;charset=utf-8");
		} else if (filePath.endsWith(".js")) {
			ctx.setContentType("text/javascript;charset=utf-8");
		}
		ctx.send(text);
	}

	private void returnResourceFile(CiContext ctx) throws IOException {
		String filePath = resoucePath + ctx.path().replace(basePath, "");
		returnResourceFile(ctx, filePath);
	}

	private String getRemoteAddress(CiContext ctx) {
		return ((InetSocketAddress) ctx.address()).getAddress().getHostAddress();
	}

	public boolean isPermittedRequest(String remoteAddress) {
		boolean ipV6 = remoteAddress != null && remoteAddress.indexOf(':') != -1;

		if (ipV6) {
			return "0:0:0:0:0:0:0:1".equals(remoteAddress) || (denyList.size() == 0 && allowList.size() == 0);
		}

		IPAddress ipAddress = new IPAddress(remoteAddress);

		for (IPRange range : denyList) {
			if (range.isIPAddressInRange(ipAddress)) {
				return false;
			}
		}

		if (allowList.size() > 0) {
			for (IPRange range : allowList) {
				if (range.isIPAddressInRange(ipAddress)) {
					return true;
				}
			}

			return false;
		}

		return true;
	}

	private void putSessionUser(CiContext ctx) {
		String val = randStr(3) + ":" + (new Date().getTime()) + ":" + randStr(3);
		val = xcode(val);

		ByteBuf src = Unpooled.wrappedBuffer(val.getBytes());
		ByteBuf dest = Base64.encode(src);
		byte[] ret = new byte[dest.readableBytes()];
		dest.readBytes(ret);
		src.release();
		dest.release();
		val = new String(ret);

		Cookie cookie = new DefaultCookie(SESSION_USER_KEY, val);
		cookie.setHttpOnly(true);
		ctx.setCookie(cookie);
	}

	protected boolean ContainsUser(CiContext ctx) {
		String val = ctx.getCookieValue(SESSION_USER_KEY);
		if (val == null || val.length() < 5) {
			return false;
		}

		ByteBuf src = Unpooled.wrappedBuffer(val.getBytes());
		ByteBuf dest = Base64.decode(src);
		byte[] ret = new byte[dest.readableBytes()];
		dest.readBytes(ret);
		src.release();
		dest.release();
		val = new String(ret);

		val = xcode(val);
		int begin = val.indexOf(':');
		int end = val.lastIndexOf(':');
		if (begin < 0 || end < 0 || begin >= end) {
			return false;
		}
		val = val.substring(begin + 1, end);
		try {
			long t = Long.parseLong(val);
			long e = new Date().getTime();
			long tt = e - t;
			// System.out.println(tt);
			// System.out.println(7200*1000);
			long exptime = 7200 * 1000;
			if (tt > 0 && tt < exptime) {
				if (tt < (exptime / 2)) {
					putSessionUser(ctx);
				}
				return true;
			}
		} catch (NumberFormatException e) {
		}
		return false;
	}

	protected static void addIpRule(String param, List<IPRange> to) {
		try {
			if (param != null && param.trim().length() != 0) {
				param = param.trim();
				String[] items = param.split(",");

				for (String item : items) {
					if (item == null || item.length() == 0) {
						continue;
					}

					IPRange ipRange = new IPRange(item);
					to.add(ipRange);
				}
			}
		} catch (Exception e) {
			logger.error(e);
		}
	}

	private static String randStr(int len) {
		String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
		Random random = new Random();
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < len; i++) {
			int number = random.nextInt(base.length());
			sb.append(base.charAt(number));
		}
		return sb.toString();
	}

	protected static String xcode(String s) {
		return xcode(s, "~ml@pik!or~*)-+w+@gb>");
	}

	protected static String xcode(String s, String key) {
		if (key == null || key.length() == 0 || s == null || s.length() == 0)
			return s;
		int ls = s.length();
		key = fixKey(key, ls);
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < ls; i++) {
			char sc = s.charAt(i);
			char sk = key.charAt(i);
			char r = (char) (sc ^ sk);
			sb.append(r);
		}
		return sb.toString();
	}

	private static String fixKey(String k, int l) {
		StringBuilder sb = new StringBuilder(k);
		int num = (int) Math.ceil(l / k.length());
		for (int i = 0; i < num; i++) {
			sb.append(k);
		}
		sb.setLength(l);
		return sb.toString();
	}
}
